#======================================================================
# Product:      KnowledgeEngine (Plone Version)
#
# File:         BlogPost.py
#
# Copyright:    Copyright (c) 2006, Faulkner Technologies
#
# Author:       John Meredith
#
# Description:  
#======================================================================


#======================================================================
# KnowledgeEngine Imports
#----------------------------------------------------------------------
from Products.KnowledgeEngine.Common import *
from Products.KnowledgeEngine.Core import *
from Products.KnowledgeEngine.Action import Action, ActionInfo
from Products.KnowledgeEngine.PropertyDefinition import *
from Products.KnowledgeEngine.PropertyTypes import *
from Products.KnowledgeEngine.PropertyFormats import *
from Products.KnowledgeEngine.PropertyStructures import *
from Products.KnowledgeEngine.KERFProcessor import *
from Clients import getBlogClients


#======================================================================
# Zope Library Imports
#----------------------------------------------------------------------
from Globals import DTMLFile
from App.ImageFile import ImageFile
from Products.CMFCore.utils import getToolByName


#======================================================================
# Python Library Imports
#----------------------------------------------------------------------
import httplib
import sha
import base64
import binascii
import time
from xmlrpclib import Fault


#======================================================================
# BlogPostException
#----------------------------------------------------------------------
class BlogPostException(Exception):
    """
        Custom exception for the BlogPost action
    """

    def __init__(self, value):
        self.value = value
        
    def __str__(self):
        return repr(self.value)


#======================================================================
# BlogPostInfo Class Definition
#----------------------------------------------------------------------
class BlogPostInfo(ActionInfo):
    """
        Defines the class info methods, defining information about
        the class without needing an instance of the class
    """
    
    #---------------------------------------------------------------#
    
    def getClassName(self):
        """
            Returns the name of the class for which 
            we are providing information.
        """
        return "BlogPost"

    #---------------------------------------------------------------#

    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """
    
        return "BlogPost"
        
    #---------------------------------------------------------------#

    def getIdentityType(self):
        """
            Return the identity type.  
        """
        
        return "blog"
        
    #---------------------------------------------------------------#
    
    def getClassIcon(self):
        """
            Return the appropriate icon file for the class.
        """
        
        return ImageFile('skins/BlogPost_icon.gif', globals())
   
    #---------------------------------------------------------------#

    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register 
            class specific properties using the PropertyReflection interface.
            
            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered
            
            NOTE: This method is called before ClassInfo.onAfterStartup()            
        """

        #initialise the super-class properties for reflection
        ActionInfo.onConfigureProperties(self)

        # Registered blog clients
        blogClients = getBlogClients().keys()
        blogClients.sort()

        #add the 'blogType' property
        blogType = PropertyDefinition(
            identity="blogType",
            displayName="Blog Type",
            description="What type of blog are you posting to?",
            propertyType=pt_STRING,
            propertyFormat=pf_NATIVE,
            hasEnumeratedValues=true,
            enumeratedValuesList=blogClients,
            includeBlankOption=true,
            isMandatory=true,
        )
        self.addPropertyDefinition(blogType)
        
        #add the 'URL' property
        self.addPropertyDefinition(
            PropertyDefinition(
                identity="blogURL",
                displayName="Blog URL",
                description="URL of remote blog site",
                defaultValue="",
                propertyType=pt_STRING,
                propertyFormat=pf_TEXT_SINGLELINE,
                propertyStructure=ps_ATOMIC,
                isMandatory=true,
            )
        )
        
        # #add the 'blogID' property        
        self.addPropertyDefinition(
            PropertyDefinition(
                identity="blogID",
                displayName="Blog ID",
                description="Your weblog ID (optional, but helpful if you have it)",
                defaultValue="",
                propertyType=pt_STRING,
                propertyFormat=pf_TEXT_SINGLELINE,
                propertyStructure=ps_ATOMIC,
            )
        )
        
        #add the 'category' property        
        self.addPropertyDefinition(
            PropertyDefinition(
                identity="category",
                displayName="Category",
                description="The category each post will be assigned to. This is usually the integer ID",
                defaultValue="",
                propertyType=pt_STRING,
                propertyFormat=pf_TEXT_SINGLELINE,
                propertyStructure=ps_ATOMIC,
            )
        )
        
        #add the 'isAnonymous' property
        isAnonymous = PropertyDefinition(
            identity="isAnonymous",
            displayName="Post anonymously?",
            description="Check this if your blog software allows anonymous posting. Note: Most don't",
            defaultValue="",
            propertyType=pt_BOOLEAN,
            propertyFormat=pf_CHECKBOX,
            jsInitialise="Event.observe('keisAnonymous', 'click', function(event){ Element.toggle('usernameField','passwordField'); });"
        )
        self.addPropertyDefinition(isAnonymous)
        
        #add the 'username' property        
        self.addPropertyDefinition(
            PropertyDefinition(
                identity="username",
                displayName="Username",
                description="Your weblog username",
                defaultValue="",
                propertyType=pt_STRING,
                propertyFormat=pf_TEXT_SINGLELINE,
                propertyStructure=ps_ATOMIC,
                isMandatory=true,
                jsInitialise="if ($('keisAnonymous').checked == true) { Element.hide('usernameField'); }"
            )
        )
        
        #add the 'password' property        
        self.addPropertyDefinition(
            PropertyDefinition(
                identity="password",
                displayName="Password",
                description="Your weblog password",
                defaultValue="",
                propertyType=pt_STRING,
                propertyFormat=pf_TEXT_SINGLELINE,
                propertyStructure=ps_ATOMIC,
                isMandatory=true,
                jsInitialise="if ($('keisAnonymous').checked == true) { Element.hide('passwordField'); }"
            )
        )
        
        #add the 'title' property        
        self.addPropertyDefinition(
            PropertyDefinition(
                identity="postTitle",
                displayName="Title",
                description="The title of your blog post (KERF Friendly)",
                defaultValue="",
                propertyType=pt_STRING,
                propertyFormat=pf_TEXT_MULTILINE,
                propertyStructure=ps_ATOMIC
            )
        )
        
        #add the 'text' property        
        self.addPropertyDefinition(
            PropertyDefinition(
                identity="text",
                displayName="Text",
                description="The text to post (KERF Friendly)",
                defaultValue="The actual text you'd like to post",
                propertyType=pt_STRING,
                propertyFormat=pf_TEXT_MULTILINE,
                propertyStructure=ps_ATOMIC
            )
        )
        
        #add the 'publish' property        
        self.addPropertyDefinition(
            PropertyDefinition(
                identity="publishImmediately",
                displayName="Publish",
                description="Publish after post?",
                defaultValue=true,
                propertyType=pt_BOOLEAN,
                propertyFormat=pf_CHECKBOX,
                propertyStructure=ps_ATOMIC
            )
        )

    #---------------------------------------------------------------#

#####################################################################
## BlogPost Action Class Definition
class BlogPost(Action):
    """
        A class to represent a simple folder that may contain Elements
    """

    #---------------------------------------------------------------#

    #ABSTRACT TRANSIENT: a ClassInfo implementation instance to provide info about this class
    _CLASSINFO = BlogPostInfo()
    
    #---------------------------------------------------------------#

    def __init__(self, id, identity):
        "Constructor for the Folder"        

        Action.__init__(self, id, identity)
                
        #perform onAfterInstanciation processing
        self.onAfterInstanciation()

    #---------------------------------------------------------------#

    def onAfterInstanciation(self):
        """
            Handle Knowledge Engine specific actions after this object 
            has been loaded into memory.  
            
            This is useful for performing backwards compatibility
        """        
        
        #perform default processing
        Action.onAfterInstanciation(self)

    #---------------------------------------------------------------#

    def onAfterIdentityChange(self, identityMappings):
        """
            Handle Knowledge Engine specific actions after the 
            identity of this object has changed.
            
            The identityMappings dictionary holds 
            mappings from oldIdentities -> newIdentities
            (including this object)
                        
        """

        #perform default processing
        Action.onAfterIdentityChange(self, identityMappings)
        
        #update the KERF friendly properties
        self.text = updateKERF(self.text, identityMappings)
        
    #---------------------------------------------------------------#
    
    def execute(self, assessment, user = None, previousActionSuccess = true):
        """
           Send post to blog server
        """    
        DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"

        #### construct the post
        
        # Quills expects the title and body
        # of a post to be combined, separated by '\n' as
        # Blogger API doesn't support titles
        # add datetime to differentiate titles - Quills barfs on posts with the same title
        title = str(resolveKERF(self.postTitle, assessment))
        if not title:
            title = "posted from KE - set a title in your BlogPost action to replace this text"
        new_title = str(self.ZopeTime().ISO()) + ' ' + title + '\n'

        
        #----------------------------------------------------------------------
        # Construct the post body - we'll prepend a bunch of boilerplate before
        # the main body of post
        #----------------------------------------------------------------------
        model = str(assessment.getModel().getTitleOrId())
        name = str(assessment.getName())
        instantCreated = str(assessment.getInstantCreated().strftime(DATETIME_FORMAT))
        instantModified = str(assessment.getInstantModified().strftime(DATETIME_FORMAT))
        if assessment.getIsLocked():
            status = "Locked"
        else:
            status = "Unlocked"
        user = str(assessment.getCreatedBy())

        # get score from scored report
        modelObj = assessment.getModel()
        up = getToolByName(self, 'knowledgeengine_userproviderservice')
        userObj = up.getUser(user)
        reports = modelObj.getReports(userObj, 'SingleAssessmentReport')
        if reports:
            scored_reports = [r for r in reports if r.getClassInfo().getIdentityType() == 'scoredreport']
            raw_score = scored_reports[0].getScore(assessment)
            score = str(raw_score)
            raw_total = scored_reports[0].getMaximumPossibleScore()
            total = str(raw_total)
            percentage = '%.2f' % (raw_score / raw_total * 100.0)
        else:
            total = percentage = 'N/A'
            score = 'N/A (need to add a scored report to model to get score)'
        
        # assemble blog post
        body = model + ' - ' + name + '\n'
        body = body + "Created on " + instantCreated + '\n'
        body = body + "Last Modified on " + instantModified + '\n'
        body = body + "Current Status: " + status + '\n'
        body = body + "User: " + user + '\n'
        body = body + "Score: " + score + ' out of a possible ' + total + ' (' + percentage + '%)''\n\n'
        body = body + str(resolveKERF(self.text, assessment))


        #----------------------------------------------------------------------
        # Post to the selected blog
        #----------------------------------------------------------------------
        blogType = self.getPropertyValue("blogType")
        url = self.getPropertyValue("blogURL")

        if not self.getPropertyValue('isAnonymous'):
            blog = getBlogClients()[blogType](
                username=self.getPropertyValue("username"),
                password=self.getPropertyValue("password"),
                url=url
            )
        else:
            blog = getBlogClients()[blogType](url=url)

        # Now post to the blog
        try:
            postId = blog.createPost(
                blogId=self.getPropertyValue("blogID"),
                title=new_title,
                content=body,
                category=self.getPropertyValue("category"),
                publish=self.getPropertyValue("publishImmediately")
            )
            
            print "Post ID: %s" % (postId, )
        #except Fault, f:
        #    print "XMLRPC Error: %s [%s]" % (f.faultString, f.faultCode)
        except BlogPostException, e:
            print "BlogPostException: %s" % (e.args, )

        # return that the action did something
        return true

            
#####################################################################
## Class Initialisation

# Register Class Information with the Knowledge Engine
registerGenericObjectClass(BlogPost)

#####################################################################